
#include <QtCore>
#include <QtGui>
#include <QMenu>

#include "base/utils/utils.h"
#include "hardware/Gps/GPS.h"
#include "uav/utils_mavlink.h"

#include "MapWidget.h"

using namespace std;
using namespace pi;
using namespace mapcontrol;


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

struct MapType_Data
{
    char    name[256];
    int     typeID;
};

#define MAPTYPE_STRUCT(n)   { #n, core::MapType::n }

static MapType_Data g_arrMapType[] =
{
    MAPTYPE_STRUCT(GoogleMap),
    MAPTYPE_STRUCT(GoogleSatellite),
    MAPTYPE_STRUCT(GoogleLabels),
    MAPTYPE_STRUCT(GoogleTerrain),
    MAPTYPE_STRUCT(GoogleHybrid),

    MAPTYPE_STRUCT(GoogleMapChina),
    MAPTYPE_STRUCT(GoogleSatelliteChina),
    MAPTYPE_STRUCT(GoogleLabelsChina),
    MAPTYPE_STRUCT(GoogleTerrainChina),
    MAPTYPE_STRUCT(GoogleHybridChina),

    MAPTYPE_STRUCT(OpenStreetMap),
    MAPTYPE_STRUCT(OpenStreetOsm),
    MAPTYPE_STRUCT(OpenStreetMapSurfer),
    MAPTYPE_STRUCT(OpenStreetMapSurferTerrain),

    MAPTYPE_STRUCT(YahooMap),
    MAPTYPE_STRUCT(YahooSatellite),
    MAPTYPE_STRUCT(YahooLabels),
    MAPTYPE_STRUCT(YahooHybrid),

    MAPTYPE_STRUCT(BingMap),
    MAPTYPE_STRUCT(BingSatellite),
    MAPTYPE_STRUCT(BingHybrid),

    MAPTYPE_STRUCT(ArcGIS_Map),
    MAPTYPE_STRUCT(ArcGIS_Satellite),
    MAPTYPE_STRUCT(ArcGIS_ShadedRelief),
    MAPTYPE_STRUCT(ArcGIS_Terrain),
    //MAPTYPE_STRUCT(ArcGIS_WorldTopo),

    MAPTYPE_STRUCT(ArcGIS_MapsLT_Map),
    MAPTYPE_STRUCT(ArcGIS_MapsLT_OrtoFoto),
    MAPTYPE_STRUCT(ArcGIS_MapsLT_Map_Labels),
    MAPTYPE_STRUCT(ArcGIS_MapsLT_Map_Hybrid),

    MAPTYPE_STRUCT(PergoTurkeyMap),
    MAPTYPE_STRUCT(SigPacSpainMap),

    MAPTYPE_STRUCT(GoogleMapKorea),
    MAPTYPE_STRUCT(GoogleSatelliteKorea),
    MAPTYPE_STRUCT(GoogleLabelsKorea),
    MAPTYPE_STRUCT(GoogleHybridKorea),

    MAPTYPE_STRUCT(YandexMapRu),
    //MAPTYPE_STRUCT(Statkart_Topo2),

    {"NULL", -1}
};

char *getMapName_fromID(core::MapType::Types t)
{
    int     i = 0;

    while(1) {
        if( g_arrMapType[i].typeID == t )
            return g_arrMapType[i].name;

        if( g_arrMapType[i].typeID < 0 ) return NULL;

        i ++;
    }

    return NULL;
}


MapType_Dialog::MapType_Dialog(QWidget *parent) : QDialog(parent)
{
    this->setWindowTitle("Select Map Type");

    setupUi();

    setupMapType_list();
    setMapType(core::MapType::GoogleSatellite);
}

void MapType_Dialog::setupUi(void)
{
    if (this->objectName().isEmpty())
        this->setObjectName(QString::fromUtf8("MapWidget_MapTypeDiag"));
    this->setFixedSize(374, 122);

    buttonBox = new QDialogButtonBox(this);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(20, 80, 341, 32));
    buttonBox->setOrientation(Qt::Horizontal);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

    labMapType = new QLabel(this);
    labMapType->setObjectName(QString::fromUtf8("labMapType"));
    labMapType->setGeometry(QRect(32, 34, 61, 15));
    labMapType->setText("Map Type:");

    cbMapType = new QComboBox(this);
    cbMapType->setObjectName(QString::fromUtf8("cbMapType"));
    cbMapType->setGeometry(QRect(100, 30, 251, 25));

    QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    QMetaObject::connectSlotsByName(this);
}

void MapType_Dialog::setupMapType_list(void)
{
    int     i;

    i = 0;
    while(1) {
        if( g_arrMapType[i].typeID >= 0 )
            cbMapType->addItem(g_arrMapType[i].name);
        else
            break;

        i ++;
    }
}

void MapType_Dialog::setMapType(core::MapType::Types t)
{
    int i = 0;

    while(1) {
        if( g_arrMapType[i].typeID == t ) {
            cbMapType->setCurrentIndex(i);
            return;
        }

        if( g_arrMapType[i].typeID == -1 ) break;

        i ++;
    }

    // set default map
    cbMapType->setCurrentIndex(0);
}

core::MapType::Types MapType_Dialog::getMapType(void)
{
    int     idx, typeID;

    idx    = cbMapType->currentIndex();
    typeID = g_arrMapType[cbMapType->currentIndex()].typeID;

    //printf("currentIdx = %4d, typeID = %4d, mapName = %s\n",
    //       idx, typeID, g_arrMapType[idx].name);

    return (core::MapType::Types) typeID;
}



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

WaypointEdit_Dialog::WaypointEdit_Dialog(QWidget *parent) : QDialog(parent)
{
    this->setWindowTitle("Waypoints Edit");

    setupUi();
}

void WaypointEdit_Dialog::setupUi(void)
{
    if (this->objectName().isEmpty())
        this->setObjectName(QString::fromUtf8("WaypointEdit_Dialog"));
    this->resize(695, 439);


    //////////////////////////////////////////////////////////
    /// WayPoint edit
    //////////////////////////////////////////////////////////
    wpEdit = new UI_waypointEdit(this);


    //////////////////////////////////////////////////////////
    /// ok/cancle buttonbox
    //////////////////////////////////////////////////////////
    buttonBox = new QDialogButtonBox(this);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setOrientation(Qt::Horizontal);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

    QObject::connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    QObject::connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));


    //////////////////////////////////////////////////////////
    /// layout
    //////////////////////////////////////////////////////////
    verticalLayout = new QVBoxLayout(this);
    verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));

    verticalLayout->addWidget(wpEdit);
    verticalLayout->addWidget(buttonBox);


    QMetaObject::connectSlotsByName(this);
}

int WaypointEdit_Dialog::setWaypoints(int idx, QMap<int, mapcontrol::WayPointItem*> *wpMap,
                                      int heightAltitude)
{
    return wpEdit->setWaypoints(idx, wpMap, heightAltitude);
}

int WaypointEdit_Dialog::updateWaypoints(void)
{
    return wpEdit->updateWaypoints();
}

void WaypointEdit_Dialog::setReferenceAltitude(double alt)
{
    return wpEdit->setReferenceAltitude(alt);
}

double WaypointEdit_Dialog::getReferenceAltitude(void)
{
    return wpEdit->getReferenceAltitude();
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

MapWidget::MapWidget(QWidget *parent) :
    mapcontrol::OPMapWidget(parent)
{
    m_conf          = NULL;
    m_uasManager    = NULL;

    m_giMsgBoard    = NULL;
    m_giStatusText  = NULL;
    m_giVideoFrame  = NULL;
    m_giLogInfo     = NULL;

    configuration->SetAccessMode(core::AccessMode::ServerAndCache);
    configuration->SetTileMemorySize(200);
    configuration->SetCacheLocation(
                QString::fromStdString(svar.GetString("FastGCS.MapWidget.cacheLocation",
                                                      "./Data/FastGCS/map/")));

    SetZoom(4);
    SetMinZoom(4);
    SetMaxZoom(18);
    SetMapType(MapType::BingSatellite);
    SetShowTileGridLines((bool) svar.GetInt("FastGCS.ui.MapWidget.ShowTileGridLines", 0));

    // set initial values
    m_bSelectArea = 0;
    m_pSelectArea1.SetLat(-9999);   m_pSelectArea1.SetLng(-9999);
    m_pSelectArea2.SetLat(-9999);   m_pSelectArea2.SetLng(-9999);

    m_homeShow = 1;
    m_homeAlt = 440;
    m_homePos.SetLat(-9999);        m_homePos.SetLng(-9999);
    m_homeSafearea = 100;

    m_gcsShow = 1;
    m_gcsAlt = 440;
    m_gcsPos.SetLat(-9999);         m_gcsPos.SetLng(-9999);
    m_gcsSafearea = 100;

    m_videoFrameShowHide = 1;
    m_videoFrameScale = svar.GetDouble("FrameTracker.ShowFrameScale", 0.5);

    m_flightHeight = 50;

    // setup menus
    setupMenu();

    // open default graphic items
    setShowMsgBoard(true);
    setShowVideoFrame(true);
    if( svar.GetInt("FastGCS.ui.MapWidget.StatusText.show", 1) )
        setShowStatusText(true);
    if( svar.GetInt("FastGCS.ui.MapWidget.LogInfo.show", 1) )
        setShowLogInfo(true);

    // regist log information handle
    LogInfo_Message_Handle h = tr1::bind(&MapWidget::logMessageHandle,
                                         this, std::tr1::placeholders::_1);
    dbg_registMessageHandle("MapWidget", h);
}

MapWidget::~MapWidget()
{
    dbg_unregistMessageHandle("MapWidget");
}


int MapWidget::setupMenu(void)
{
    #define INSERT_ACTION_MAP(x) m_actionMap.insert(#x, x)

    // setup actions
    QAction* actionMapType = new QAction(tr("Map Type"), this);
    connect(actionMapType, SIGNAL(triggered()),
            this, SLOT(actMapType_SelectMap()));
    INSERT_ACTION_MAP(actionMapType);


    QAction* actionMapAccess_ServerAndCache = new QAction(tr("ServerAndCache"), this);
    actionMapAccess_ServerAndCache->setCheckable(true);
    actionMapAccess_ServerAndCache->setChecked(false);
    connect(actionMapAccess_ServerAndCache, SIGNAL(triggered()),
            this, SLOT(actMapAccess_ServerAndCache()));
    INSERT_ACTION_MAP(actionMapAccess_ServerAndCache);

    QAction* actionMapAccess_Cache = new QAction(tr("Cache"), this);
    actionMapAccess_Cache->setCheckable(true);
    actionMapAccess_Cache->setChecked(true);
    connect(actionMapAccess_Cache, SIGNAL(triggered()),
            this, SLOT(actMapAccess_Cache()));
    INSERT_ACTION_MAP(actionMapAccess_Cache);


    QAction* actionCacheMap = new QAction(tr("Cache Map"), this);
    connect(actionCacheMap, SIGNAL(triggered()),
            this, SLOT(actCacheMap()));
    INSERT_ACTION_MAP(actionCacheMap);

    QAction* actionMoveTo = new QAction(tr("Move To"), this);
    actionMoveTo->setStatusTip(tr("Move to"));
    connect(actionMoveTo, SIGNAL(triggered()),
            this, SLOT(actMoveTo()));
    INSERT_ACTION_MAP(actionMoveTo);

    QAction* actionSetTargetGlobal = new QAction(tr("Set Target Global"), this);
    actionSetTargetGlobal->setStatusTip(tr("Set current target in GPS coord and move to it"));
    connect(actionSetTargetGlobal, SIGNAL(triggered()),
            this, SLOT(actSetTargetGlobal()));
    INSERT_ACTION_MAP(actionSetTargetGlobal);

    QAction* actionChangeHeight = new QAction(tr("Change Height"), this);
    actionChangeHeight->setStatusTip(tr("Change height to"));
    connect(actionChangeHeight, SIGNAL(triggered()),
            this, SLOT(actChangeHeight()));
    INSERT_ACTION_MAP(actionChangeHeight);

    QAction* actionCircularFlight = new QAction(tr("Circular Flight"), this);
    actionCircularFlight->setStatusTip(tr("Circular Flight"));
    connect(actionCircularFlight, SIGNAL(triggered()),
            this, SLOT(actSetCircularFlight()));
    INSERT_ACTION_MAP(actionCircularFlight);

    QAction* actionStopCircularFlight = new QAction(tr("Circular Flight Stop"), this);
    actionStopCircularFlight->setStatusTip(tr("Stop Circular Flight"));
    connect(actionStopCircularFlight, SIGNAL(triggered()),
            this, SLOT(actStopCircularFlight()));
    INSERT_ACTION_MAP(actionStopCircularFlight);


    QAction* actionWaypoint_add = new QAction(tr("Waypoint Add"), this);
    actionWaypoint_add->setStatusTip(tr("Add a new waypoint"));
    connect(actionWaypoint_add, SIGNAL(triggered()),
            this, SLOT(actWaypoint_add()));
    INSERT_ACTION_MAP(actionWaypoint_add);

    QAction* actionWaypoint_del = new QAction(tr("Waypoint Delete"), this);
    actionWaypoint_del->setStatusTip(tr("Delelete selected waypoints"));
    connect(actionWaypoint_del, SIGNAL(triggered()),
            this, SLOT(actWaypoint_del()));
    INSERT_ACTION_MAP(actionWaypoint_del);

    QAction* actionWaypoint_edit = new QAction(tr("Waypoint Edit"), this);
    actionWaypoint_edit->setStatusTip(tr("Edit waypoints"));
    connect(actionWaypoint_edit, SIGNAL(triggered()),
            this, SLOT(actWaypoint_edit()));
    INSERT_ACTION_MAP(actionWaypoint_edit);

    QAction* actionWaypoint_clear = new QAction(tr("Waypoint Clear"), this);
    actionWaypoint_clear->setStatusTip(tr("Clear waypoints"));
    connect(actionWaypoint_clear, SIGNAL(triggered()),
            this, SLOT(actWaypoint_clear()));
    INSERT_ACTION_MAP(actionWaypoint_clear);

    QAction* actionWaypoint_save = new QAction(tr("Waypoint Save"), this);
    actionWaypoint_save->setStatusTip(tr("Save waypoints to file"));
    actionWaypoint_save->setShortcut(QKeySequence::fromString(tr("ctrl+s")));
    connect(actionWaypoint_save, SIGNAL(triggered()),
            this, SLOT(actWaypoint_save()));
    INSERT_ACTION_MAP(actionWaypoint_save);

    QAction* actionWaypoint_load = new QAction(tr("Waypoint Load"), this);
    actionWaypoint_load->setStatusTip(tr("Load waypoints from file"));
    actionWaypoint_load->setShortcut(QKeySequence::fromString(tr("ctrl+l")));
    connect(actionWaypoint_load, SIGNAL(triggered()),
            this, SLOT(actWaypoint_load()));
    INSERT_ACTION_MAP(actionWaypoint_load);


    QAction* actionUAVTrail_clear = new QAction(tr("Clear UAV trail"), this);
    actionUAVTrail_clear->setStatusTip(tr("Clear UAVs trails and lines"));
    actionUAVTrail_clear->setShortcut(QKeySequence::fromString(tr("ctrl+c")));
    connect(actionUAVTrail_clear, SIGNAL(triggered()),
            this, SLOT(actUAVTrail_clear()));
    INSERT_ACTION_MAP(actionUAVTrail_clear);


    QAction* actionSelectArea_beg = new QAction(tr("SelectArea Begin"), this);
    connect(actionSelectArea_beg, SIGNAL(triggered()),
            this, SLOT(actSelectArea_beg()));
    INSERT_ACTION_MAP(actionSelectArea_beg);

    QAction* actionSelectArea_end = new QAction(tr("SelectArea End"), this);
    connect(actionSelectArea_end, SIGNAL(triggered()),
            this, SLOT(actSelectArea_end()));
    INSERT_ACTION_MAP(actionSelectArea_end);

    QAction* actionSelectArea_clear = new QAction(tr("SelectArea Clear"), this);
    connect(actionSelectArea_clear, SIGNAL(triggered()),
            this, SLOT(actSelectArea_clear()));
    INSERT_ACTION_MAP(actionSelectArea_clear);


    QAction* actionHome_Set = new QAction(tr("Set Home"), this);
    connect(actionHome_Set, SIGNAL(triggered()),
            this, SLOT(actHome_Set()));
    INSERT_ACTION_MAP(actionHome_Set);

    QAction* actionHome_Safearea = new QAction(tr("Set Home Safearea"), this);
    connect(actionHome_Safearea, SIGNAL(triggered()),
            this, SLOT(actHome_Safearea()));
    INSERT_ACTION_MAP(actionHome_Safearea);

    QAction* actionHome_ShowHide = new QAction(tr("Show/Hide Home"), this);
    actionHome_ShowHide->setCheckable(true);
    actionHome_ShowHide->setChecked(true);
    connect(actionHome_ShowHide, SIGNAL(triggered()),
            this, SLOT(actHome_ShowHide()));
    INSERT_ACTION_MAP(actionHome_ShowHide);


    QAction* actionGCS_Set = new QAction(tr("Set GCS"), this);
    connect(actionGCS_Set, SIGNAL(triggered()),
            this, SLOT(actGCS_Set()));
    INSERT_ACTION_MAP(actionGCS_Set);

    QAction* actionGCS_Safearea = new QAction(tr("Set GCS Safearea"), this);
    connect(actionGCS_Safearea, SIGNAL(triggered()),
            this, SLOT(actGCS_Safearea()));
    INSERT_ACTION_MAP(actionGCS_Safearea);

    QAction* actionGCS_ShowHide = new QAction(tr("Show/Hide GCS"), this);
    actionGCS_ShowHide->setCheckable(true);
    actionGCS_ShowHide->setChecked(true);
    connect(actionGCS_ShowHide, SIGNAL(triggered()),
            this, SLOT(actGCS_ShowHide()));
    INSERT_ACTION_MAP(actionGCS_ShowHide);

    QAction* actionGotoHome = new QAction(tr("Goto Home"), this);
    connect(actionGotoHome, SIGNAL(triggered()),
            this, SLOT(actGotoHome()));
    INSERT_ACTION_MAP(actionGotoHome);

    QAction* actionGotoGCS = new QAction(tr("Goto GCS"), this);
    connect(actionGotoGCS, SIGNAL(triggered()),
            this, SLOT(actGotoGCS()));
    INSERT_ACTION_MAP(actionGotoGCS);


    // setup menu
    m_popupMenu = new QMenu("Menu");

    m_popupMenu->addAction(actionMoveTo);
    m_popupMenu->addAction(actionSetTargetGlobal);
    m_popupMenu->addAction(actionChangeHeight);
    m_popupMenu->addAction(actionCircularFlight);
    m_popupMenu->addAction(actionStopCircularFlight);

    m_popupMenu->addSeparator();
    m_popupMenu->addAction(actionWaypoint_add);
    m_popupMenu->addAction(actionWaypoint_del);
    m_popupMenu->addAction(actionWaypoint_edit);
    m_popupMenu->addAction(actionWaypoint_clear);
    m_popupMenu->addAction(actionWaypoint_save);
    m_popupMenu->addAction(actionWaypoint_load);

    m_actWaypointEnd = m_popupMenu->addSeparator();
    m_popupMenu->addAction(actionGotoHome);
    m_popupMenu->addAction(actionGotoGCS);

    m_popupMenu->addSeparator();
    m_popupMenu->addAction(actionGCS_Set);
    m_popupMenu->addAction(actionGCS_Safearea);
    m_popupMenu->addAction(actionGCS_ShowHide);

    // more utils menu
    m_popupMenu->addSeparator();

    QMenu *menuUtils = m_popupMenu->addMenu("More ...");
    menuUtils->addAction(actionMapType);
    QMenu *menuAccessMode = menuUtils->addMenu("Access Type ...");
    menuAccessMode->addAction(actionMapAccess_ServerAndCache);
    menuAccessMode->addAction(actionMapAccess_Cache);
    menuUtils->addAction(actionCacheMap);

    menuUtils->addSeparator();
    menuUtils->addAction(actionHome_Set);
    menuUtils->addAction(actionHome_Safearea);
    menuUtils->addAction(actionHome_ShowHide);

    menuUtils->addSeparator();
    menuUtils->addAction(actionSelectArea_beg);
    menuUtils->addAction(actionSelectArea_end);
    menuUtils->addAction(actionSelectArea_clear);

    menuUtils->addSeparator();
    menuUtils->addAction(actionUAVTrail_clear);

    return 0;
}

int MapWidget::setupExMenu(QMap<QString, QAction*> &mainMenu)
{
    QList<QAction*> acts;

    acts.push_back(mainMenu["actUploadWaypoints"]);
    acts.push_back(mainMenu["actDownloadWaypoints"]);
    acts.push_back(mainMenu["actClearWaypoints"]);
    acts.push_back(mainMenu["actSetCurrentWP"]);

    m_popupMenu->insertActions(m_actWaypointEnd, acts);

    return 0;
}

int MapWidget::setupMainMenu(QVector<QMenu*> &menuList)
{
    QMenu *menuMain = m_popupMenu->addMenu("Main ...");

    QVector<QMenu*>::iterator it;
    for(it=menuList.begin(); it!=menuList.end(); it++) {
        menuMain->addMenu(*it);
    }

    return 0;
}



QAction* MapWidget::getAction(const QString &actName)
{
    QMap<QString, QAction*>::iterator iter;

    iter = m_actionMap.find(actName);
    if( iter == m_actionMap.end() ) {
        return NULL;
    } else {
        return iter.value();
    }
}

void MapWidget::setConf(QSettings *conf)
{
    m_conf = conf;

    if( m_conf == NULL ) return;

    // map type & access mode
    {
        MapType::Types              mapType;
        core::AccessMode::Types     accessMode;
        QString                     cacheLocation;

        // load settings
        accessMode    = (core::AccessMode::Types) m_conf->value("MapWidget.accessMode",
                                                                (int)(core::AccessMode::ServerAndCache)).toInt();
        mapType       = (MapType::Types) m_conf->value("MapWidget.mapType",
                                                       (int)(MapType::BingSatellite)).toInt();
        cacheLocation = QString::fromStdString(svar.GetString("FastGCS.MapWidget.cacheLocation",
                                                              "./Data/FastGCS/map/"));

        // set configurations
        configuration->SetAccessMode(accessMode);
        configuration->SetCacheLocation(cacheLocation);
        SetMapType(mapType);

        // set accessMode actions
        if( accessMode == core::AccessMode::ServerAndCache ) {
            getAction("actionMapAccess_ServerAndCache")->setChecked(true);
            getAction("actionMapAccess_Cache")->setChecked(false);
        } else if ( accessMode == core::AccessMode::CacheOnly ) {
            getAction("actionMapAccess_ServerAndCache")->setChecked(false);
            getAction("actionMapAccess_Cache")->setChecked(true);
        }
    }

    // home & GCS settings
    {
        // home
        m_homeShow = m_conf->value("MapWidget.HOME.show", m_homeShow).toInt();
        m_homePos.SetLat(m_conf->value("MapWidget.HOME.lat", m_homePos.Lat()).toDouble());
        m_homePos.SetLng(m_conf->value("MapWidget.HOME.lng", m_homePos.Lng()).toDouble());
        m_homeAlt = m_conf->value("MapWidget.HOME.alt", m_homeAlt).toDouble();
        m_homeSafearea = m_conf->value("MapWidget.HOME.safeArea", m_homeSafearea).toDouble();

        if( m_homeShow ) {
            this->SetShowHome(true);
            this->Home->SetCoord(m_homePos);
            this->Home->SetAltitude( (int)(m_homeAlt) );
            this->Home->SetSafeArea((int)(m_homeSafearea));

            getAction("actionHome_ShowHide")->setChecked(true);
        } else {
            getAction("actionHome_ShowHide")->setChecked(false);
        }

        // GCS
        m_gcsShow = m_conf->value("MapWidget.GCS.show", m_gcsShow).toInt();
        m_gcsPos.SetLat(m_conf->value("MapWidget.GCS.lat", m_gcsPos.Lat()).toDouble());
        m_gcsPos.SetLng(m_conf->value("MapWidget.GCS.lng", m_gcsPos.Lng()).toDouble());
        m_gcsAlt = m_conf->value("MapWidget.GCS.alt", m_gcsAlt).toDouble();
        m_gcsSafearea = m_conf->value("MapWidget.GCS.safeArea", m_gcsSafearea).toDouble();

        if( m_gcsShow ) {
            this->SetShowGCS(true);
            this->GCS->SetCoord(m_gcsPos);
            this->GCS->SetAltitude( (int)(m_gcsAlt) );
            this->GCS->SetSafeArea((int)(m_gcsSafearea));

            getAction("actionGCS_ShowHide")->setChecked(true);
        } else {
            getAction("actionGCS_ShowHide")->setChecked(false);
        }
    }

    {
        // default flight height
        m_flightHeight = m_conf->value("MapWidget.flightHeight", m_flightHeight).toDouble();
    }

    {
        m_videoFrameShowHide = m_conf->value("MapWidget.videoFrame.Show", m_videoFrameShowHide).toInt();
        //m_videoFrameScale = m_conf->value("MapWidget.videoFrame.Scale", m_videoFrameScale).toDouble();

        setShowVideoFrame(m_videoFrameShowHide);
        if( m_giVideoFrame != NULL )
            m_giVideoFrame->setImageScale(m_videoFrameScale);
    }
}

void MapWidget::syncConf(void)
{
    if( m_conf == NULL ) return;

    // map type & access mode
    MapType::Types              mapType;
    core::AccessMode::Types     accessMode;

    mapType = GetMapType();
    accessMode = configuration->AccessMode();
    m_conf->setValue("MapWidget.mapType", (int)(mapType));
    m_conf->setValue("MapWidget.accessMode", (int)(accessMode));

    // home
    m_conf->setValue("MapWidget.HOME.show", m_homeShow);
    m_conf->setValue("MapWidget.HOME.lat", m_homePos.Lat());
    m_conf->setValue("MapWidget.HOME.lng", m_homePos.Lng());
    m_conf->setValue("MapWidget.HOME.alt", m_homeAlt);
    m_conf->setValue("MapWidget.HOME.safeArea", m_homeSafearea);

    // home
    m_conf->setValue("MapWidget.GCS.show", m_gcsShow);
    m_conf->setValue("MapWidget.GCS.lat", m_gcsPos.Lat());
    m_conf->setValue("MapWidget.GCS.lng", m_gcsPos.Lng());
    m_conf->setValue("MapWidget.GCS.alt", m_gcsAlt);
    m_conf->setValue("MapWidget.GCS.safeArea", m_gcsSafearea);

    m_conf->setValue("MapWidget.flightHeight", m_flightHeight);

    // video frame
    m_conf->setValue("MapWidget.videoFrame.Show", m_videoFrameShowHide);
    //m_conf->setValue("MapWidget.videoFrame.Scale", m_videoFrameScale);


    // sync to configure file
    m_conf->sync();
}


void MapWidget::reset(void)
{
    // clear all UAV
    QList<UAVItem*> lstUAV = GetUAVS();
    foreach(UAVItem* u, lstUAV) {
        u->DeleteTrail();
        delete u;
    }

    UAVS.clear();
}

void MapWidget::setHome(internals::PointLatLng &p, double alt)
{
    m_homePos = p;
    m_homeAlt = alt;

    if( m_homeShow ) {
        this->SetShowHome(true);
        this->Home->SetCoord(m_homePos);
        this->Home->SetAltitude( (int)(m_homeAlt) );
        this->Home->SetSafeArea( (int)(m_homeSafearea) );
    }

    // update all waypoint's home alt
    QMap<int, WayPointItem*> wpAll = WPAll();
    for(int i=0; i<wpAll.size(); i++) {
        wpAll[i]->SetHomeAlt(m_homeAlt);

        if( i == 0 ) wpAll[0]->SetCoord(m_homePos);
    }

    syncConf();

    ReloadMap();
}

void MapWidget::getHome(internals::PointLatLng &p, double &alt)
{
    p   = m_homePos;
    alt = m_homeAlt;
}

void MapWidget::setGCS(internals::PointLatLng &p, double alt)
{
    m_gcsPos = p;
    m_gcsAlt = alt;

    if( m_gcsShow ) {
        this->SetShowGCS(true);
        this->GCS->SetCoord(m_gcsPos);
        this->GCS->SetAltitude( (int)(m_gcsAlt) );
        this->GCS->SetSafeArea( (int)(m_gcsSafearea) );
    }

    syncConf();

    ReloadMap();
}

void MapWidget::getGCS(internals::PointLatLng &p, double &alt)
{
    p   = m_gcsPos;
    alt = m_gcsAlt;
}


void MapWidget::gotoHome(void)
{
    SetCurrentPosition(m_homePos);
}

void MapWidget::gotoGCS(void)
{
    SetCurrentPosition(m_gcsPos);
}


int MapWidget::getWaypoints(AP_MissionArray &wpa)
{
    QMap<int, mapcontrol::WayPointItem*> wpMap;

    // get waypoints
    wpMap  = WPAll();

    foreach(mapcontrol::WayPointItem* p, wpMap) {
        AP_MissionItem wp;

        wp.idx      = p->Number();
        wp.lng      = p->Coord().Lng();
        wp.lat      = p->Coord().Lat();
        wp.alt      = p->Height();
        wp.heading  = p->Heading();
        wp.cmd      = mavlink_mav_str2cmd(p->WaypointType().toStdString());

        wpa.set(wp);
    }

    return 0;
}

int MapWidget::setUASManager(UAS_Manager *u)
{
    m_uasManager = u;

    if( m_giMsgBoard != NULL )  m_giMsgBoard->setUASManager(m_uasManager);
    if( m_giStatusText != NULL) m_giStatusText->setUASManager(m_uasManager);
    return 0;
}

int MapWidget::setShowMsgBoard(bool s)
{
    if( s && !m_giMsgBoard ) {
        m_giMsgBoard=new MsgBoardItem();
        //m_giMsgBoard->setScale(0.1+0.05*(qreal)(this->size().width())/1000*(qreal)(this->size().height())/600);
        //    compass->setTransformOriginPoint(compass->boundingRect().width(),compass->boundingRect().height());

        if( svar.GetInt("MapWidget.MsgBoard.moveable", 0) )
            m_giMsgBoard->setFlag(QGraphicsItem::ItemIsMovable, true);
        else
            m_giMsgBoard->setFlag(QGraphicsItem::ItemIsMovable, false);

        mscene.addItem(m_giMsgBoard);
        m_giMsgBoard->setTransformOriginPoint(0, 0);
        m_giMsgBoard->setPos(140, 15);
        m_giMsgBoard->setZValue(3);
        m_giMsgBoard->setOpacity(1);
        m_giMsgBoard->setUASManager(m_uasManager);
    }

    if(!s && m_giMsgBoard) {
        delete m_giMsgBoard;
        m_giMsgBoard=0;
    }
}

int MapWidget::setShowStatusText(bool s)
{
    if( s && !m_giStatusText ) {
        m_giStatusText=new StatusTextItem();
        //m_giMsgBoard->setScale(0.1+0.05*(qreal)(this->size().width())/1000*(qreal)(this->size().height())/600);

        if( svar.GetInt("MapWidget.StatusText.moveable", 0) )
            m_giStatusText->setFlag(QGraphicsItem::ItemIsMovable, true);
        else
            m_giStatusText->setFlag(QGraphicsItem::ItemIsMovable, false);

        mscene.addItem(m_giStatusText);
        m_giStatusText->setTransformOriginPoint(0, 0);
        m_giStatusText->setPos(570, 15);
        m_giStatusText->setZValue(3);
        m_giStatusText->setOpacity(1);
        m_giStatusText->setUASManager(m_uasManager);
    }

    if(!s && m_giStatusText) {
        delete m_giStatusText;
        m_giStatusText=0;
    }
}

int MapWidget::setShowVideoFrame(bool s)
{
    if( s && !m_giVideoFrame ) {
        m_giVideoFrame = new VideoFrameItem(this);
        //m_giVideoFrame->setFlag(QGraphicsItem::ItemIsMovable, true);
        mscene.addItem(m_giVideoFrame);
        m_giVideoFrame->setTransformOriginPoint(0, 0);
        m_giVideoFrame->setZValue(2);
        m_giVideoFrame->setOpacity(1);
        m_giVideoFrame->setImageScale(m_videoFrameScale);
        m_giVideoFrame->updatePos();

        m_videoFrameShowHide = 1;
        syncConf();
    }

    if(!s && m_giVideoFrame) {
        delete m_giVideoFrame;
        m_giVideoFrame=0;

        m_videoFrameShowHide = 0;
        syncConf();
    }
}

int MapWidget::setVideoFrame(QImage &img)
{
    if( m_giVideoFrame == NULL ) return -1;

    m_giVideoFrame->setImage(&img);

    return 0;
}

/*
int MapWidget::setVideoFrame(cv::Mat *img)
{
    if( m_giVideoFrame == NULL ) return -1;

    m_giVideoFrame->setImage(img);

    return 0;
}
*/


int MapWidget::setVideoFrameScale(double s)
{
    m_videoFrameScale = s;
    syncConf();

    if( m_giVideoFrame == NULL ) return -1;

    m_giVideoFrame->setImageScale(s);

    return 0;
}

int MapWidget::setVideoFrameScaleUpDown(int updown)
{
    m_videoFrameScale += 0.05*updown/abs(updown);
    if( m_videoFrameScale < 0.05 ) m_videoFrameScale = 0.05;
    if( m_videoFrameScale > 2.0 )  m_videoFrameScale = 2.0;

    syncConf();

    if( m_giVideoFrame == NULL ) return -1;

    m_giVideoFrame->setImageScale(m_videoFrameScale);

    return 0;
}


int MapWidget::setShowLogInfo(bool s)
{
    if( s && !m_giLogInfo ) {
        m_giLogInfo = new LogInfoItem(this);
        //m_giLogInfo->setFlag(QGraphicsItem::ItemIsMovable, true);
        mscene.addItem(m_giLogInfo);
        m_giLogInfo->setTransformOriginPoint(0, 0);
        m_giLogInfo->setZValue(5);
        m_giLogInfo->setOpacity(1);
        m_giLogInfo->updatePos();
    }

    if(!s && m_giLogInfo) {
        delete m_giLogInfo;
        m_giLogInfo = NULL;
    }
}


int MapWidget::setGuidedTarget(double lat, double lng)
{
    internals::PointLatLng pt;

    pt.SetLat(lat);
    pt.SetLng(lng);

    if( !this->ShowGuidedTarget() ) SetShowGuidedTarget(true);

    this->GTI->SetCoord(pt);
    this->GTI->RefreshPos();
    this->GTI->update();

    return 0;
}

int MapWidget::getGuidedTarget(double &lat, double &lng)
{
    if( this->ShowGuidedTarget() ) {
        internals::PointLatLng pt = this->GTI->Coord();

        lat = pt.Lat();
        lng = pt.Lng();
    }

    return 0;
}



int MapWidget::logMessageHandle(std::string &msg)
{
    if( m_giLogInfo ) m_giLogInfo->insertMessage(msg);

    return 0;
}


int MapWidget::setWaypoints(AP_MissionArray &wpa)
{
    // delete all exist waypoints
    delAllWaypointLines();
    WPDeleteAll();

    // insert each waypoint
    for(int i=0; i<wpa.size(); i++) {
        AP_MissionItem *p = wpa.get(i);

        addMissionItem(*p);
    }

    return 0;
}

int MapWidget::addMissionItem(AP_MissionItem &_wp)
{
    internals::PointLatLng  p;
    WayPointItem            *wp1 = NULL, *wp2 = NULL;

    p.SetLat(_wp.lat);
    p.SetLng(_wp.lng);

    // add home waypoint
    QMap<int, WayPointItem*> wpAll = WPAll();
    if( wpAll.size() == 0 ) {
        // create home waypoint
        mapcontrol::WayPointItem *wp = this->WPCreate();

        wp->SetCoord(m_homePos);
        wp->SetHomeAlt(m_homeAlt);
        wp->SetHeight(0);
        wp->SetHeading(_wp.heading);
        wp->SetMoveable(false);
        wp->SetWaypointType("Home");

        wp1 = wp;

        //connect(wp, SIGNAL(WPEdit(int, WayPointItem*)),
        //        this, SLOT(actWPEdit(int, WayPointItem*)));
    } else {
        // add desired waypoint
        mapcontrol::WayPointItem *wp = this->WPCreate();
        wp->SetCoord(p);
        wp->SetHomeAlt(m_homeAlt);
        wp->SetHeight(_wp.alt);
        wp->SetHeading(_wp.heading);
        wp->SetWaypointType(QString::fromStdString(mavlink_mav_cmd2str(_wp.cmd)));
        wp2 = wp;

        // add waypoint line
        wp1 = wp->getPreviousWaypoint();
        if( wp1 != NULL ) addWaypointLine(wp1, wp2);

        connect(wp, SIGNAL(WPEdit(int, WayPointItem*)),
                this, SLOT(actWPEdit(int, WayPointItem*)));
    }

    return 0;
}

int MapWidget::delMissionItem(AP_MissionItem &_wp)
{
    QMap<int, WayPointItem*> wpMap = WPAll();
    QMap<int, WayPointItem*>::iterator it = wpMap.find(_wp.idx);

    if( it != wpMap.end() ) {
        WayPointItem* wp = it.value();

        // do not delete wp[0]
        if( wp->Number() == 0 ) return 0;

        delWaypointLines(wp);
        WPDelete(wp);
    }

    return 0;
}


void MapWidget::mouseMoveEvent(QMouseEvent *event)
{
    OPMapWidget::mouseMoveEvent(event);

    svar.GetDouble("FastGCS.MapWidget.curLat", 0) = currentmouseposition.Lat();
    svar.GetDouble("FastGCS.MapWidget.curLng", 0) = currentmouseposition.Lng();

    if( ShowDistanceMeasure() ) {
        DMI->SetRemoteCoord(currentmouseposition);
        ReloadMap();
    }
}

void MapWidget::mousePressEvent(QMouseEvent *event)
{
    if( event->button() == Qt::RightButton ) {
        m_popupMenu->popup(event->globalPos());
    } else {
        mapcontrol::OPMapWidget::mousePressEvent(event);
    }
}

void MapWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    if( event->button() == Qt::MidButton ) {
        if( ShowDistanceMeasure() ) {
            SetShowDistanceMeasure(false);
        } else {
            SetShowDistanceMeasure(true);
            DMI->SetCoord(currentmouseposition);
            DMI->SetRemoteCoord(currentmouseposition);
            DMI->RefreshPos();
        }

        ReloadMap();
    } else if( event->button() == Qt::LeftButton ) {
        if( !(event->modifiers() & Qt::ControlModifier) ) return;

        UAS_MAV *m = NULL;
        if( m_uasManager ) m = m_uasManager->get_active_mav();
        if( m == NULL ) return;

        // if current mode is not 'STABILZIED', then quit
        // FIXME: GUIDED mode for APM
        if( m->customMode != MFM_STABILIZED ) return;

        // move to new position
        setGuidedTarget(currentmouseposition.Lat(), currentmouseposition.Lng());

        if( !m->m_guidedControl.posSetted )
            m->m_guidedControl.setPosition(currentmouseposition.Lat(), currentmouseposition.Lng(), m->gpH);

        m->m_guidedControl.turnOn();
        m->m_guidedControl.lat = currentmouseposition.Lat();
        m->m_guidedControl.lng = currentmouseposition.Lng();
        m->setGuidedTarget(currentmouseposition.Lat(), currentmouseposition.Lng(), m->m_guidedControl.alt,
                           UAS_MAV::GCS_MOUSE);
    }
}

void MapWidget::wheelEvent(QWheelEvent *event)
{
    QWheelEvent e(event->pos(), event->delta(),
                  event->buttons(), event->modifiers());

    QGraphicsView::wheelEvent(&e);
}


void MapWidget::resizeEvent(QResizeEvent *event)
{
    OPMapWidget::resizeEvent(event);

    if( m_giStatusText != NULL && 0) {
        m_giStatusText->setPos(width()/2 - m_giStatusText->boundingRect().width()/2,
                               height() - m_giStatusText->boundingRect().height());
    }

    if( m_giVideoFrame != NULL ) {
        m_giVideoFrame->updatePos();
    }

    if( m_giLogInfo != NULL ) {
        m_giLogInfo->updatePos();
    }
}


void MapWidget::actMapType_SelectMap(void)
{
    MapType_Dialog          diag;
    core::MapType::Types    mt, mt0;

    // set current map type active
    mt0 = GetMapType();
    diag.setMapType(mt0);

    // open maptype selecting dialog
    if( QDialog::Accepted == diag.exec() ) {
        mt = diag.getMapType();

        if( mt0 == mt ) return;

        // set new map type
        SetMapType(mt);

        // update settings
        if( m_conf != NULL ) {
            m_conf->setValue("MapWidget.mapType", (int)(mt));
            m_conf->sync();
        }

        // reload map
        ReloadMap();
    }
}


void MapWidget::actMapAccess_ServerAndCache(void)
{
    getAction("actionMapAccess_ServerAndCache")->setChecked(true);
    getAction("actionMapAccess_Cache")->setChecked(false);

    configuration->SetAccessMode(core::AccessMode::ServerAndCache);

    if( m_conf != NULL ) {
        m_conf->setValue("MapWidget.accessMode", (int)(core::AccessMode::ServerAndCache));
        m_conf->sync();
    }
}

void MapWidget::actMapAccess_Cache(void)
{
    getAction("actionMapAccess_ServerAndCache")->setChecked(false);
    getAction("actionMapAccess_Cache")->setChecked(true);

    configuration->SetAccessMode(core::AccessMode::CacheOnly);

    if( m_conf != NULL ) {
        m_conf->setValue("MapWidget.accessMode", (int)(core::AccessMode::CacheOnly));
        m_conf->sync();
    }
}

void MapWidget::actCacheMap(void)
{
    internals::RectLatLng  rect;

    rect = this->SelectedArea();
    if( rect.IsEmpty() ) {
        QMessageBox msgbox(QMessageBox::Warning,
                           "Warning", "Please select an area first!");
        msgbox.exec();

        return;
    }

    this->RipMap();
}

void MapWidget::actMoveTo(void)
{
    internals::PointLatLng  p = currentMousePosition();

    // show guide marker
    if( !this->ShowGuidedTarget() ) SetShowGuidedTarget(true);

    this->GTI->SetCoord(p);
    this->GTI->RefreshPos();
    this->GTI->update();

    UAS_MAV *m = NULL;
    if( m_uasManager ) m = m_uasManager->get_active_mav();
    if( m == NULL ) return;
    m->moveTo(p.Lat(), p.Lng(), m->gpH);
    dbg_pi("UAV[%d] moves to target.", m->ID);
}

void MapWidget::actSetTargetGlobal(void)
{
    internals::PointLatLng  p = currentMousePosition();

    // show guide marker
    // consider using different marker to prevent contradicion to actMoveTo()
    if( !this->ShowGuidedTarget() ) SetShowGuidedTarget(true);

    this->GTI->SetCoord(p);
    this->GTI->RefreshPos();
    this->GTI->update();

    UAS_MAV *m = NULL;
    if( m_uasManager ) m = m_uasManager->get_active_mav();
    if( m == NULL ) return;
    m->setTargetGlobal(p.Lat(), p.Lng(), m->gpH);
    dbg_pi("UAV[%d] set target global to target and begin move.", m->ID);
}

void MapWidget::actChangeHeight(void)
{
    UAS_MAV *m = NULL;
    if( m_uasManager ) m = m_uasManager->get_active_mav();
    if( m == NULL ) return;

    // if current mode is not 'LOITER', then quit
    //if( m->customMode != mavlink_px4_custommode_getInt("AUTO_LOITER") ) return;

    // get new height
    double newH = m->gpH;
    bool ok = true;
    newH = QInputDialog::getDouble(this,
                                   tr("New height"),
                                   tr("Height (m):"),
                                   newH, 5, 3000,
                                   1,
                                   &ok);

    // change height
    if( !ok ) return;

    // save new height
    if( SvarWithType<GPS_Reader*>::instance().exist("GPS_Reader.ptr") ) {
        if( SvarWithType<GPS_Reader*>::instance()["GPS_Reader.ptr"] != NULL ) {
            m->m_guidedControl.alt = newH;
            dbg_pi("Set FollowMe Alt=%6.1f", newH);
            return;
        }
    }

    // send change height command
    m->m_guidedControl.turnOn();
    if( !m->m_guidedControl.posSetted ) {
        m->m_guidedControl.setPosition(m->gpLat, m->gpLon, newH);
    }

    m->setGuidedTarget(m->m_guidedControl.lat, m->m_guidedControl.lng, newH,
                       UAS_MAV::GCS_MOUSE,
                       1);                                                      // ignore input source check
}

void MapWidget::actSetCircularFlight(void)
{
    // check MAV exist or not
    UAS_MAV *m = NULL;
    if( m_uasManager ) m = m_uasManager->get_active_mav();
    if( m == NULL ) return;

    // get new radius
    double &radius = svar.GetDouble("UAS.circularFlight.radius", 5);
    bool ok = true;
    double nR = QInputDialog::getDouble(this,
                                        tr("Circle Flight Radius:"),
                                        tr("Radius (m):"),
                                        radius, 3, 100,
                                        1,
                                        &ok);
    // change height
    if( !ok ) return;
    radius = nR;

    // draw circularFlight marker
    if( !this->ShowCircularFlight() ) SetShowCircularFlight(true);

    QColor clMarker(0, 0, 255);
    {
        string szMarkerColor = svar.GetString("UAS.circularFlight.markerColor", "0,0,255");
        StringArray saColor = split_text(szMarkerColor, ",");

        if( saColor.size() >= 3 ) {
            clMarker.setRed(str_to_int(saColor[0]));
            clMarker.setGreen(str_to_int(saColor[1]));
            clMarker.setBlue(str_to_int(saColor[2]));
        }
    }

    this->CFI->setColor(clMarker);
    this->CFI->SetCoord(currentmouseposition);
    this->CFI->setRadius(radius);
    this->CFI->RefreshPos();
    this->CFI->update();

    // do circular flight
    m->doCircularFlight(currentmouseposition.Lat(), currentmouseposition.Lng(), radius);
}

void MapWidget::actStopCircularFlight(void)
{
    // check MAV exist or not
    UAS_MAV *m = NULL;
    if( m_uasManager ) m = m_uasManager->get_active_mav();
    if( m == NULL ) return;

    // draw circularFlight marker
    if( this->ShowCircularFlight() ) {
        SetShowCircularFlight(false);
        ReloadMap();
    }

    // stop circular flight
    m->stopCircularFlight();
}

void MapWidget::actWaypoint_add(void)
{
    internals::PointLatLng  p = currentMousePosition();
    AP_MissionItem wp;

    QMap<int, WayPointItem*> wpAll = WPAll();

    wp.lat = p.Lat();
    wp.lng = p.Lng();
    addMissionItem(wp);

    if( wpAll.size() == 0 ) addMissionItem(wp);
}

void MapWidget::actWaypoint_del(void)
{
    QList<mapcontrol::WayPointItem*>    wpList;
    int                     i, n;

    // set selected waypoints
    wpList = WPSelected();
    n = wpList.size();

    for(i=0; i<n; i++) {
        mapcontrol::WayPointItem *wp = wpList[i];
        AP_MissionItem w;

        w.idx = wp->Number();
        delMissionItem(w);
    }
}

void MapWidget::actWaypoint_edit(void)
{
    QMap<int, mapcontrol::WayPointItem*>    wpMap;
    QList<mapcontrol::WayPointItem*>        wpList;
    int                                     idx;

    WaypointEdit_Dialog                     wpDialog;

    wpMap  = WPAll();
    wpList = WPSelected();

    wpDialog.setReferenceAltitude(m_homeAlt);

    if( wpList.size() == 1 && 0 ) {
        idx = wpList[0]->Number();

        wpDialog.setWaypoints(idx, &wpMap);
    } else {
        wpDialog.setWaypoints(-1, &wpMap);
    }

    // update edited data
    if( QDialog::Accepted == wpDialog.exec() ) {
        wpDialog.updateWaypoints();
    }
}

void MapWidget::actWaypoint_clear(void)
{
    delAllWaypointLines();
    WPDeleteAll();

    ReloadMap();
}

void MapWidget::actWPEdit(int num, WayPointItem *wp)
{
    QMap<int, mapcontrol::WayPointItem*>    wpMap;
    int                                     idx;

    WaypointEdit_Dialog                     wpDialog;

    wpMap  = WPAll();
    idx    = num;

    wpDialog.setReferenceAltitude(m_homeAlt);
    wpDialog.setWaypoints(idx, &wpMap);

    if( QDialog::Accepted == wpDialog.exec() ) {
        wpDialog.updateWaypoints();
    }
}


void MapWidget::actWaypoint_save(void)
{
    QMap<int, mapcontrol::WayPointItem*> wpMap;

    string dp = svar.GetString("FastGCS.DataPath", "./Data2/FastGCS");
    string path = path_join(dp, "waypoints");

    QString fname;

    // get waypoints
    wpMap  = WPAll();

    // get save filename
    fname = QFileDialog::getSaveFileName(this, tr("Waypoint file"),
                                         QString::fromStdString(path),
                                         tr("Waypoint Files (*.wp)"));

    if( fname.size() < 1 ) return;
    if( !fname.endsWith(".wp", Qt::CaseInsensitive) ) {
        fname = fname + ".wp";
    }

    // copy to AP_MissionArray
    AP_MissionArray  arrWP;

    foreach(mapcontrol::WayPointItem* p, wpMap) {
        AP_MissionItem wp;

        wp.idx      = p->Number();
        wp.lng      = p->Coord().Lng();
        wp.lat      = p->Coord().Lat();
        wp.alt      = p->Height();
        wp.heading  = p->Heading();
        wp.cmd      = mavlink_mav_str2cmd(p->WaypointType().toStdString());

        arrWP.set(wp);
    }

    // save to file
    arrWP.save(fname.toStdString());
}

void MapWidget::actWaypoint_load(void)
{
    string dp = svar.GetString("FastGCS.DataPath", "./Data/FastGCS");
    string path = path_join(dp, "waypoints");

    QString fname;

    AP_MissionArray                 arrWP;
    AP_MissionItemMap               *wpMap;

    // get save filename
    fname = QFileDialog::getOpenFileName(this, tr("Waypoint file"),
                                         QString::fromStdString(path),
                                         tr("Waypoint Files (*.wp)"));

    if( fname.size() < 1 ) return;

    // load AP_MissionArray
    if( 0 != arrWP.load(fname.toStdString()) ) return;

    // delete exist waypoints
    this->WPDeleteAll();
    delAllWaypointLines();

    // insert missions to mapWidget
    wpMap = arrWP.getAll();

    for(int i=0; i<wpMap->size(); i++) {
        AP_MissionItem *mi = wpMap->at(i);

        addMissionItem(*mi);
    }

    // repaint map
    ReloadMap();
}


void MapWidget::actUAVTrail_clear(void)
{
    QList<UAVItem*> lstUAVs = GetUAVS();

    foreach(UAVItem* uav, lstUAVs) {
        uav->DeleteTrail();
    }
}


void MapWidget::actSelectArea_beg(void)
{
    internals::PointLatLng p;

    p = currentMousePosition();

    m_pSelectArea1.SetLat(p.Lat());
    m_pSelectArea1.SetLng(p.Lng());
}

void MapWidget::actSelectArea_end(void)
{
    internals::PointLatLng p;

    double  lat1, lat2;
    double  lng1, lng2;

    p = currentMousePosition();

    m_pSelectArea2.SetLat(p.Lat());
    m_pSelectArea2.SetLng(p.Lng());

    if( m_pSelectArea1.Lat() < -1000 || m_pSelectArea1.Lng() < -1000 )
        return;

    if( m_pSelectArea1.Lat() > m_pSelectArea2.Lat() ) {
        lat1 = m_pSelectArea1.Lat();
        lat2 = m_pSelectArea2.Lat();
    } else {
        lat1 = m_pSelectArea2.Lat();
        lat2 = m_pSelectArea1.Lat();
    }

    if( m_pSelectArea1.Lng() < m_pSelectArea2.Lng() ) {
        lng1 = m_pSelectArea1.Lng();
        lng2 = m_pSelectArea2.Lng();
    } else {
        lng1 = m_pSelectArea2.Lng();
        lng2 = m_pSelectArea1.Lng();
    }


    internals::RectLatLng rect(lat1, lng1, fabs(lng2 - lng1), fabs(lat2 - lat1));
    SetSelectedArea(rect);

    ReloadMap();
}

void MapWidget::actSelectArea_clear(void)
{
    internals::RectLatLng rect;

    SetSelectedArea(rect);

    ReloadMap();
}

void MapWidget::actHome_Set(void)
{
    internals::PointLatLng p;

    p = currentMousePosition();

    // get home altitude
    bool ok = true;
    double alt = QInputDialog::getDouble(this,
                                         tr("Input altitude of home"),
                                         tr("Altitude:"),
                                         m_homeAlt, -1000, 10000,
                                         1,
                                         &ok);
    if ( ok ) m_homeAlt = alt;
    else return;

    // set home position, altitude, safearea
    this->SetShowHome(true);
    this->Home->SetCoord(p);
    this->Home->SetAltitude((int)(m_homeAlt));
    this->Home->SetSafeArea((int)(m_homeSafearea));
    m_homePos = p;

    // update all waypoint's home alt
    QMap<int, WayPointItem*> wpAll = WPAll();
    for(int i=0; i<wpAll.size(); i++) {
        wpAll[i]->SetHomeAlt(m_homeAlt);

        if( i == 0 ) wpAll[0]->SetCoord(m_homePos);
    }


    // write configure
    syncConf();

    // refresh map
    ReloadMap();

    // send home set signal
    emit HomeSet();
}

void MapWidget::actHome_Safearea(void)
{
    bool ok = true;
    double d = QInputDialog::getDouble(this,
                                       tr("Set safe area"),
                                       tr("Radius (m):"),
                                       m_homeSafearea, 20, 50000,
                                       1,
                                       &ok);
    if ( ok ) m_homeSafearea = d;
    else return;

    // write configure
    syncConf();

    if( Home != NULL ) {
        this->Home->SetSafeArea((int)(m_homeSafearea));

        // refresh map
        ReloadMap();
    }
}

void MapWidget::actHome_ShowHide(void)
{
    if( m_homeShow ) m_homeShow = 0;
    else             m_homeShow = 1;

    if( m_homeShow ) {
        setHome(m_homePos, m_homeAlt);

        getAction("actionHome_ShowHide")->setChecked(true);
    } else {
        this->SetShowHome(false);

        getAction("actionHome_ShowHide")->setChecked(false);
    }

    // write configure
    syncConf();
}

void MapWidget::actGCS_Set(void)
{
    internals::PointLatLng p;

    p = currentMousePosition();

    // get home altitude
    bool ok = true;
    double alt = QInputDialog::getDouble(this,
                                         tr("Input altitude of GCS"),
                                         tr("Altitude:"),
                                         m_gcsAlt, -1000, 10000,
                                         1,
                                         &ok);
    if ( ok ) m_gcsAlt = alt;
    else return;

    // set home position, altitude, safearea
    this->SetShowGCS(true);
    this->GCS->SetCoord(p);
    this->GCS->SetAltitude((int)(m_gcsAlt));
    this->GCS->SetSafeArea((int)(m_gcsSafearea));
    m_gcsPos = p;

    // write configure
    syncConf();

    // refresh map
    ReloadMap();

    // send GCS set signal
    emit GCSSet();
}

void MapWidget::actGCS_Safearea(void)
{
    bool ok = true;
    double d = QInputDialog::getDouble(this,
                                       tr("Set safe area of GCS"),
                                       tr("Radius (m):"),
                                       m_gcsSafearea, 20, 50000,
                                       1,
                                       &ok);
    if ( ok ) m_gcsSafearea = d;
    else return;

    // write configure
    syncConf();

    if( GCS != NULL ) {
        this->GCS->SetSafeArea((int)(m_gcsSafearea));

        // refresh map
        ReloadMap();
    }
}

void MapWidget::actGCS_ShowHide(void)
{
    if( m_gcsShow ) m_gcsShow = 0;
    else            m_gcsShow = 1;

    if( m_gcsShow ) {
        setGCS(m_gcsPos, m_gcsAlt);

        getAction("actionGCS_ShowHide")->setChecked(true);
    } else {
        this->SetShowGCS(false);

        getAction("actionGCS_ShowHide")->setChecked(false);
    }

    // write configure
    syncConf();
}

void MapWidget::actGotoHome(void)
{
    gotoHome();
}

void MapWidget::actGotoGCS(void)
{
    gotoGCS();
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

MsgBoardItem::MsgBoardItem()
{
    m_uasManager = NULL;

    w = 400;
    h = 100;

    startTimer(200);
}

void MsgBoardItem::timerEvent(QTimerEvent *event)
{
    update();
}


void MsgBoardItem::paint(QPainter *painter,
                         const QStyleOptionGraphicsItem *option, QWidget *widget)
{

    Q_UNUSED(option);
    Q_UNUSED(widget);

    // get active uav & telem
    if( m_uasManager == NULL ) return;
    UAS_Base *u = m_uasManager->get_active_mav();
    UAS_Telem *t = m_uasManager->get_active_telem();

    // draw background
    painter->setPen(QColor(0x0, 0x0, 0x0));
    painter->setBrush(QBrush(QColor(0x0, 0x0, 0x0, 0x60)));
    painter->drawRoundedRect(0, 0, w, h, 5, 5);

    // draw important imformation
    {
        QPen   blackPen(Qt::white);
        blackPen.setWidth(2);

        int     ix, iy;
        int     fontSize = 12;
        double  lineSpace = 1.8;
        char    buf[256];

        painter->setPen(blackPen);
        painter->setFont(QFont("", fontSize));

        ix = 5;
        iy = fontSize*lineSpace;

        // draw current UAV information
        if( u != NULL )
        {
            sprintf(buf, "GPS(nSat=%d, HDOP=%4.1f, FIX=%d)", u->nSat, u->HDOP_h, u->gpsFixType);
            painter->drawText(ix, iy, buf);
            iy += fontSize*lineSpace;

            sprintf(buf, "Distance=(%1.0f, %1.0f), BatV=%5.1f (%2.0f%%)",
                    u->dis, u->disLOS,
                    u->battVolt, u->battRemaining);
            painter->drawText(ix, iy, buf);
            iy += fontSize*lineSpace;

            sprintf(buf, "VelH=%9.2f, VelV=%9.2f", u->velH, u->velV);
            painter->drawText(ix, iy, buf);
            iy += fontSize*lineSpace;
        }

        // draw RSSI
        if( t != NULL )
        {
            sprintf(buf, "RSSI=%d(%5.1f%%), %d(%5.1f%%)",
                    t->radioRSSI, t->radioRSSI_per,
                    t->radioRSSI_remote, t->radioRSSI_remote_per);
            painter->drawText(ix, iy, buf);
        }

        // draw battery remainning power with a bar
        if( u != NULL )
        {
            int ix, iy;
            int bw, bw_sep, bh, bh_sep;

            bw = 20;
            bw_sep = 7;
            bh_sep = 7;
            bh = h - 2*bh_sep;

            ix = w - bw - bw_sep;
            iy = bh_sep;

            // draw boundary
            painter->setPen(QColor(0xff, 0xff, 0xff, 0xff));
            painter->setBrush(QBrush(QColor(0x0, 0xff, 0x0, 0x00)));
            painter->drawRect(ix-1, iy-1, bw+2, bh+2);

            // draw bar
            int barHeight = 1.0 * bh * u->battRemaining / 100.0;
            iy += bh - barHeight;

            if( u->battRemaining < svar.GetDouble("Mavlink.Battery.warningPercentage", 30) )
            {
                QColor cl = QColor(0xFF, 0x2E, 0x45, 0x60);
                painter->setPen(cl);
                painter->setBrush(QBrush(cl));
            }
            else
            {
                QColor cl = QColor(0x0, 0xff, 0x0, 0x60);
                painter->setPen(cl);
                painter->setBrush(QBrush(cl));
            }

            painter->drawRect(ix, iy, bw, barHeight);
        }
    }
}

QRectF MsgBoardItem::boundingRect() const
{
    return QRectF(0, 0, w, h);
}



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

StatusTextItem::StatusTextItem()
{
    m_uasManager = NULL;

    w = 600;
    h = 100;

    startTimer(200);
}

void StatusTextItem::timerEvent(QTimerEvent *event)
{
    if( svar.GetInt("FastGCS.ui.MapWidget.StatusText.show", 1) )
        update();
}


void StatusTextItem::paint(QPainter *painter,
                           const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    if( !svar.GetInt("FastGCS.ui.MapWidget.StatusText.show", 1) ) return;

    // draw background
    if( 0 ) {
        painter->setPen(QColor(0x0, 0x0, 0x0));
        painter->setBrush(QBrush(QColor(0x0, 0x0, 0x0, 0x30)));
        painter->drawRoundedRect(0, 0, w, h, 5, 5);
    } else {
        painter->setPen(QColor(0x0, 0x0, 0x0, 0x0));
        painter->setBrush(QBrush(QColor(0x0, 0x0, 0x0, 0x0)));
        painter->drawRect(0, 0, w, h);
    }

    // get active uav
    if( m_uasManager == NULL ) return;
    UAS_Base  *u = m_uasManager->get_active_mav();
    UAS_Telem *t = m_uasManager->get_active_telem();
    UAS_GCS   *g = m_uasManager->get_active_gcs();

    // draw important imformation
    {
        QPen   blackPen(Qt::white);
        QPen   redPen(Qt::red);

        int     ix, iy;
        int     fontSize = 14;
        double  lineSpace = 1.8;
        char    buf[256];

        blackPen.setWidth(2);
        redPen.setWidth(2);
        painter->setPen(blackPen);
        painter->setFont(QFont("", fontSize));

        ix = 5;
        iy = fontSize*lineSpace;

        if( u != NULL ) {
            if( strlen(u->statusText) > 0 ) {
                sprintf(buf, "M[%d, S%d]: %s", u->ID, u->severity, u->statusText);
                if( u->severity < 2 ) painter->setPen(blackPen);
                else painter->setPen(redPen);
                painter->drawText(ix, iy, buf);

                iy += fontSize*lineSpace;
            }
        }

        if( g != NULL ) {
            if( strlen(g->statusText) > 0 ) {
                sprintf(buf, "G[%d, S%d]: %s", g->ID, g->severity, g->statusText);
                if( g->severity < 2 ) painter->setPen(blackPen);
                else painter->setPen(redPen);
                painter->drawText(ix, iy, buf);
            }
        }
    }
}

QRectF StatusTextItem::boundingRect() const
{
    return QRectF(0, 0, w, h);
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

VideoFrameItem::VideoFrameItem(MapWidget *parent)
    : m_mapView(parent)
{
    w = 960;
    h = 540;

    m_boardOffset = svar.GetInt("FastGCS.MapWidget.VideoFrameOffset", 0);

    m_imgScale = 0.2;
    m_img = NULL;

    m_tmLastImage = tm_getTimeStamp();
    m_tmUpdatePeriod = 0.02;
}

void VideoFrameItem::timerEvent(QTimerEvent *event)
{
    //update();
}

int VideoFrameItem::updatePos(void)
{
    setPos(m_boardOffset,
           m_mapView->height() - h - m_boardOffset);

    return 0;
}

int VideoFrameItem::setImage(QImage *img)
{
    int bSetPos = (m_img == NULL);
    if( img == NULL ) return -1;

    // check image insertation period
    double tmNow = tm_getTimeStamp();
    double dt = tmNow - m_tmLastImage;
    if( dt > m_tmUpdatePeriod ) {
        m_tmLastImage = tmNow;
    } else {
        return -1;
    }

    if( m_img != NULL ) delete m_img;
    m_img = new QImage(*img);

    // update bound rect
    int w_, h_;
    w_ = m_img->width();
    h_ = m_img->height();
    w = w_*m_imgScale;
    h = h_*m_imgScale;

    // reset pos
    if( bSetPos ) updatePos();

    // update
    update();

    return 0;
}

/*
int VideoFrameItem::setImage(cv::Mat *img)
{
    int bSetPos = (m_img == NULL);
    int w_, h_, c;

    // check input image
    if( img == NULL ) return -1;

    // check image insertation period
    double tmNow = tm_getTimeStamp();
    double dt = tmNow - m_tmLastImage;
    if( dt > m_tmUpdatePeriod ) {
        m_tmLastImage = tmNow;
    } else {
        return -1;
    }

    // get image size
    w_ = img->cols;
    h_ = img->rows;
    c  = img->channels();

    // delete old image
    if( m_img != NULL ) {
        delete m_img;
        m_img = NULL;
    }

    if( c == 3 ) {
        m_img = new QImage(w_, h_, QImage::Format_RGB888);

        pi::ru8 *p1 = img->data,
                *p2 = m_img->bits();

        //pi::conv_rgb888_bgr888_fast(p1, p2, w_*h_);
        conv_rgb888_bgr888(p1, p2, w_*h_);
        //memcpy(p2, p1, w_*h_*c);
    } else if( c == 1 ) {
        m_img = new QImage(w_, h_, QImage::Format_RGB888);

        pi::ru8 *p1 = img->data,
                *p2 = m_img->bits();

        for(int i=0; i<w_*h_; i++) {
            p2[0] = *p1;
            p2[1] = *p1;
            p2[2] = *p1;

            p1 ++;
            p2 += 3;
        }
    } else {
        dbg_pe("unsupport image format! c = %d\n", c);
        return -1;
    }

    // update bound rect
    w = w_*m_imgScale;
    h = h_*m_imgScale;

    // reset pos
    if( bSetPos ) updatePos();

    // update
    update();

    return 0;
}
*/

int VideoFrameItem::setImageScale(double s)
{
    m_imgScale = s;

    if( NULL == m_img ) return -1;

    // update bound rect
    int w_, h_;
    w_ = m_img->width();
    h_ = m_img->height();
    w = w_*m_imgScale;
    h = h_*m_imgScale;

    updatePos();

    // redraw image
    update();

    // update map widget
    m_mapView->ReloadMap();

    return 0;
}


void VideoFrameItem::paint(QPainter *painter,
                           const QStyleOptionGraphicsItem *option,
                           QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    if( NULL == m_img ) return;

    // get image size
    int w_, h_;
    w_ = m_img->width();
    h_ = m_img->height();
    w = w_*m_imgScale;
    h = h_*m_imgScale;

    if( w_ <= 0 || h_ <= 0 ) return;

    // get rects
    QRect rTar(0, 0, w, h), rSrc(0, 0, w_, h_);

    painter->drawImage(rTar, *m_img, rSrc);

    // draw boarder
    if( 1 ) {
        QPen pBoarder;
        pBoarder.setColor(QColor(0, 0, 0));
        pBoarder.setWidth(2);
        painter->setPen(pBoarder);
        painter->drawRect(0, 0, w, h);
    }
}

QRectF VideoFrameItem::boundingRect() const
{
    return QRectF(0, 0, w, h);
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

LogInfoItem::LogInfoItem(MapWidget *parent) : m_mapView(parent)
{
    w = svar.GetInt("FastGCS.ui.MapWidget.LogInfo.winW", 680);
    h = svar.GetInt("FastGCS.ui.MapWidget.LogInfo.winH", 200);

    m_boardOffset = svar.GetInt("FastGCS.ui.MapWidget.LogInfo.boardOffset", 10);

    m_fontName = QString::fromStdString(svar.GetString("FastGCS.ui.MapWidget.LogInfo.fontName",
                                                       "Monospace"));
    m_fontSize = 9;
    m_lineSpace = 1.5;

    m_nLines = h / (m_fontSize*m_lineSpace);

    m_tmLastMsg = tm_getTimeStamp();
    m_tmLastUpdate = tm_getTimeStamp();

    m_tmUpdatePeriod = 0.05;                                   // minimun update period (50 ms)

    m_penMap.insert(std::make_pair("ERR:", QPen(Qt::red)));
    m_penMap.insert(std::make_pair("WARN", QPen(Qt::yellow)));
    m_penMap.insert(std::make_pair("INFO", QPen(Qt::white)));
    m_penMap.insert(std::make_pair("TRAC", QPen(Qt::cyan)));

    startTimer(500);
}

void LogInfoItem::timerEvent(QTimerEvent *event)
{
    double &tmMsgPop = svar.GetDouble("FastGCS.ui.MapWidget.LogInfo.tmMsgPop", 4.0);
    double tmNow = tm_getTimeStamp();

    if( tmNow - m_tmLastMsg > tmMsgPop ) {
        pi::ScopedMutex m(m_mutexMsgPop);
        if( m_stringQueue.size() > 0 ) {
            m_stringQueue.pop_front();
            updateCanvas();
        }

        m_tmLastMsg = tmNow;
    }
}

int LogInfoItem::updatePos(void)
{
    setPos(m_mapView->width()/2 - w/2,
           m_mapView->height() - h - m_boardOffset);

    return 0;
}

int LogInfoItem::updateCanvas(void)
{
    double tmNow = tm_getTimeStamp();
    double dt = tmNow - m_tmLastUpdate;

    if( dt > m_tmUpdatePeriod ) {
        m_tmLastUpdate = tmNow;
        update();
    }

    return 0;
}


int LogInfoItem::insertMessage(std::string &msg)
{
    if( !svar.GetInt("FastGCS.ui.MapWidget.LogInfo.show", 1) )
        return 0;

    // insert to queue
    {
        pi::ScopedMutex m(m_mutexMsgPop);

        if( m_stringQueue.size() >= m_nLines ) m_stringQueue.pop_front();
        m_stringQueue.push_back(msg);
        m_tmLastMsg = tm_getTimeStamp();
    }

    // check input message period
    if( 0 ) {
        double tmNow = tm_getTimeStamp();
        double dt = tmNow - m_tmLastMsg;
        if( dt < 0.01 ) return -1;
    }

    // update canvas
    updateCanvas();

    return 0;
}

void LogInfoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    // draw background
    int &drawBackground = svar.GetInt("FastGCS.ui.MapWidget.LogInfo.drawBackground", 0);
    if( drawBackground ) {
        painter->setPen(QColor(0x0, 0x0, 0x0));
        painter->setBrush(QBrush(QColor(0x0, 0x0, 0x0, 0x30)));
        painter->drawRoundedRect(0, 0, w, h, 5, 5);
    } else {
        painter->setPen(QColor(0x0, 0x0, 0x0, 0x0));
        painter->setBrush(QBrush(QColor(0x0, 0x0, 0x0, 0x0)));
        painter->drawRect(0, 0, w, h);
    }

    // set mask
    QRegion maskRegion(1, 1, w-2, h-2, QRegion::Rectangle);
    painter->setClipRegion(maskRegion);

    // draw log information
    QPen    penNorm(Qt::lightGray);
    int     ix, iy;

    painter->setFont(QFont(m_fontName, m_fontSize));

    ix = m_boardOffset;
    iy = m_fontSize*m_lineSpace;

    {
        pi::ScopedMutex m(m_mutexMsgPop);

        for(StringQueue::iterator it=m_stringQueue.begin(); it!=m_stringQueue.end(); it++) {
            std::string &s = *it;

            painter->setPen(penNorm);

            if( s.size() >= 4 ) {
                string ss = s.substr(0, 4);
                PenMap::iterator it = m_penMap.find(ss);
                if( it != m_penMap.end() ) painter->setPen(it->second);
            }

            painter->drawText(ix, iy, QString::fromStdString(s));
            iy += m_fontSize*m_lineSpace;
        }
    }
}

QRectF LogInfoItem::boundingRect() const
{
    return QRectF(0, 0, w, h);
}
